package gu.sql2java.manager;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static gu.sql2java.SimpleLog.log;
import static gu.sql2java.SimpleLog.logString;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;

import gu.sql2java.BaseBean;
import gu.sql2java.Constant;
import gu.sql2java.RowMetaData;
import gu.sql2java.SqlRunner;
import gu.sql2java.TableManager;
import gu.sql2java.Constant.JdbcProperty;
import gu.sql2java.manager.cache.CacheManager;
import gu.sql2java.manager.cache.ColumnCache;

/**
 * 数据库操作实例({@link TableManager})管理类
 * @author guyadong
 *
 */
public class Managers implements Constant{
	/**
	 * 设置是否输出调试信息标志
	 */
	private static boolean debug = false;
	private Managers() {
	}

	private static final ImmutableMap<String, TableManager<? extends BaseBean>> 
	tableManagerInstances = loadTableManager();
	private static final  ImmutableMap<Class<?>, TableManager<? extends BaseBean>> 
	tableManagerTypeMaps = asTypeMap(tableManagerInstances);
	private static final  ImmutableMap<Class<?>, TableManager<? extends BaseBean>> 
	tableManagerBeanTypeMaps = asBeanTypeMap(tableManagerInstances);
	private static final  Map<Class<?>, TableManager<? extends BaseBean>> cacheManagers = Maps.newHashMap();
	private static final  Map<Class<?>, TableManager<? extends BaseBean>> cacheBeanTypeManagers = Maps.newHashMap();
	private static final  Map<String, TableManager<? extends BaseBean>> cacheNameManagers = Maps.newHashMap();

	/**
	 * @return 返回所有数据库操作实例(非cache)
	 */
	public static ImmutableMap<String, TableManager<? extends BaseBean>> getTableManagers() {
		return tableManagerInstances;
	}

	/**
	 * SPI(Service Provider Interface)机制加载 {@link TableManager}所有实例
	 * @return 表名和 {@link TableManager}实例的映射对象 
	 */
	private static ImmutableMap<String, TableManager<? extends BaseBean>> loadTableManager() {		
		ImmutableMap.Builder<String, TableManager<? extends BaseBean>> builder = ImmutableMap.builder();
		for(RowMetaData data:RowMetaData.tableMetadata.values()){
			try {
				TableManager<?> manager = TableManagerDecorator.makeInterfaceInstance(new BaseTableManager<>(data.tablename));
				builder.put(data.tablename,	manager);
				if(debug){
					log("TableManagerDecorator instance create for {} ",data.tablename);
				}
			} catch (RuntimeException e) {
				if(debug){
					log("FAIL TO create manager for  {} caused by {}",data.tablename,e.getMessage());
				}
			}
		}
		return builder.build();
	}

	private static final ImmutableMap<Class<?>, TableManager<? extends BaseBean>> asTypeMap(Map<String, TableManager<? extends BaseBean>>input){
		return Maps.uniqueIndex(input.values(), new Function<TableManager<? extends BaseBean>,Class<?>>(){
			@Override
			public Class<?> apply(TableManager<? extends BaseBean> input) {
				for(Class<?> clazz:input.getClass().getInterfaces()){
					if(TableManager.class.isAssignableFrom(clazz)) {
						return clazz;
					}
				}
				throw new IllegalStateException(logString("NOT FOUND immplements interface class for %s ", input.getClass()));
			}});
	}

	private static final ImmutableMap<Class<?>, TableManager<? extends BaseBean>> asBeanTypeMap(Map<String, TableManager<? extends BaseBean>>input){
		return Maps.uniqueIndex(input.values(), new Function<TableManager<? extends BaseBean>,Class<?>>(){
	
			@Override
			public Class<?> apply(TableManager<? extends BaseBean> input) {
				if(input instanceof BaseTableManager){
					return ((BaseTableManager<?>)input).metaData.beanType;
				}else if(Proxy.isProxyClass(input.getClass())){
					InvocationHandler handler = Proxy.getInvocationHandler(input);
					checkArgument(handler instanceof TableManagerDecorator,"UNKNOW HANDLER %s",handler.getClass() );
					return ((TableManagerDecorator<?>)handler).metaData.beanType;
				}
				throw new IllegalArgumentException(logString("UNKNOW SUPPORTED TableManager instance %s",input.getClass()));
			}});
	}

	/** 
	 * 根据表操作接口类型返回数据库操作实例(非cache)
	 * @param interfaceClass  接口类型
	 * @return {@link TableManager}实例，找不到则抛出异常
	 */
	@SuppressWarnings("unchecked")
	public static final <M extends TableManager<?>>M 
	getTableManager(Class<M>interfaceClass) {
		TableManager<? extends BaseBean> manager = tableManagerTypeMaps.get(interfaceClass);
		return checkNotNull((M) manager,"INVALID manager type %s",interfaceClass);
	}

	/**
	 * 根据表记录类型返回数据库操作实例(非cache)
	 * @param beanType java bean type
	 * @return {@link TableManager}实例，找不到则抛出异常
	 */
	@SuppressWarnings("unchecked")
	public static final <T extends BaseBean,M extends TableManager<T>>M 
	getTableManagerByBeanType(Class<T>beanType) {
		TableManager<? extends BaseBean> manager = tableManagerBeanTypeMaps.get(beanType);
		return checkNotNull((M) manager,"INVALID bean type %s",beanType);
	}

	/**
	 * 根据表名返回数据库操作实例(非cache)
	 * @param tablename table name
	 * @return {@link TableManager}实例，找不到则抛出异常
	 */
	@SuppressWarnings("unchecked")
	public static final <M extends TableManager<?>>M 
	getTableManager(String tablename) {
		TableManager<? extends BaseBean> manager = tableManagerInstances.get(tablename);
		return checkNotNull((M) manager,"INVALID tablename %s",tablename);
	}

	/**
	 * 根据表名返回数据库操作实例(非cache)
	 * @param tablename table name
	 * @return {@link BaseTableManager}实例，找不到则抛出异常
	 */
	@SuppressWarnings("unchecked")
	public static final <M extends BaseTableManager<?>>M 
	getBaseTableManager(String tablename) {
		return (M) baseManagerOf((TableManager<?>)getTableManager(tablename));
	}
	/**
     * 注册cache manager<br>
     * @param tablename table name
     * @param updateStrategy cache update strategy, determined  by {@link RowMetaData#hasAutoUpdateColumn} if {@code null},see also {@link ColumnCache}
     * @param maximumSize maximum capacity of cache ,{@link gu.sql2java.Constant#DEFAULT_CACHE_MAXIMUMSIZE } be used if {@code null} or <=0,see also {@link com.google.common.cache.CacheBuilder#maximumSize(long)}
     * @param duration cache data expired time,{@link gu.sql2java.Constant#DEFAULT_DURATION} be used if {@code null} or <=0,see also {@link com.google.common.cache.CacheBuilder#expireAfterAccess(long, TimeUnit)}
     * @param unit time unit for {@code duration},{@link gu.sql2java.Constant#DEFAULT_TIME_UNIT} be used if {@code null},see also {@link com.google.common.cache.CacheBuilder#expireAfterAccess(long, TimeUnit)}
     * @param weakValues use weak values,see also {@link com.google.common.cache.CacheBuilder#weakValues()}
     * @since 3.34.0
     */
	public static synchronized final <I extends TableManager<?>>
	void registerCacheManager(
			String tablename,
			com.gitee.l0km.aocache.UpdateStrategy updateStrategy,
			Long maximumSize,
			Long duration,
			TimeUnit unit, 
			Boolean weakValues){
		TableManager<?> cacheManager = CacheManager.makeCacheInstance(tablename, updateStrategy,
				CacheManager.createCacheConfig(maximumSize, duration, unit, weakValues));
		BaseTableManager<?> manager = baseManagerOf(cacheManager);
		cacheManagers.put(manager.metaData.managerInterfaceClass, cacheManager);
		cacheNameManagers.put(manager.metaData.tablename, cacheManager);
		cacheBeanTypeManagers.put(manager.metaData.beanType, cacheManager);
		if(debug){
			log("REGISTER CACHE MANAGER {}",cacheManager);
		}
	}
	/**
	 * 注册cache manager<br>
     * @param tablename table name
     * @param updateStrategy cache update strategy, determined  by {@link RowMetaData#hasAutoUpdateColumn} if {@code null},see also {@link ColumnCache}
     * @param maximumSize maximum capacity of cache ,{@link gu.sql2java.Constant#DEFAULT_CACHE_MAXIMUMSIZE } be used if {@code null} or <=0,see also {@link com.google.common.cache.CacheBuilder#maximumSize(long)}
     * @param duration cache data expired time,{@link gu.sql2java.Constant#DEFAULT_DURATION} be used if {@code null} or <=0,see also {@link com.google.common.cache.CacheBuilder#expireAfterAccess(long, TimeUnit)}
     * @param unit time unit for {@code duration},{@link gu.sql2java.Constant#DEFAULT_TIME_UNIT} be used if {@code null},see also {@link com.google.common.cache.CacheBuilder#expireAfterAccess(long, TimeUnit)}
	 * @since 3.34.0
	 */
	public static final <I extends TableManager<?>>
	void registerCacheManager(
			String tablename,
			com.gitee.l0km.aocache.UpdateStrategy updateStrategy,
			long maximumSize, 
			long duration, 
			TimeUnit unit){
		registerCacheManager(tablename, null == updateStrategy ? null : updateStrategy, maximumSize, duration, unit, true);
	}
	/**
	 * 注册cache manager<br>
	 * @param tablename table name
	 * @param updateStrategy cache update strategy, determined  by {@link RowMetaData#hasAutoUpdateColumn} if {@code null},see also {@link ColumnCache}
	 * @param maximumSize maximum capacity of cache ,{@link gu.sql2java.Constant#DEFAULT_CACHE_MAXIMUMSIZE } be used if {@code null} or <=0,see also {@link com.google.common.cache.CacheBuilder#maximumSize(long)}
	 * @param duration cache data expired time,{@link gu.sql2java.Constant#DEFAULT_DURATION} be used if {@code null} or <=0,see also {@link com.google.common.cache.CacheBuilder#expireAfterAccess(long, TimeUnit)}
	 * @param unit time unit for {@code duration},{@link gu.sql2java.Constant#DEFAULT_TIME_UNIT} be used if {@code null},see also {@link com.google.common.cache.CacheBuilder#expireAfterAccess(long, TimeUnit)}
	 * @deprecated replaced by {@link #registerCacheManager(String, com.gitee.l0km.aocache.UpdateStrategy, Long, Long, TimeUnit, Boolean)}
	 */
	public static final <I extends TableManager<?>>
	void registerCacheManager(
			String tablename,
			UpdateStrategy updateStrategy,
			long maximumSize, 
			long duration, 
			TimeUnit unit){
		registerCacheManager(tablename, null == updateStrategy ? null : updateStrategy.aous, maximumSize, duration, unit, true);
	}

	/**
	 * @return 返回所有支持缓存的数据库操作实例
	 * @see CacheManager
	 */
	public static Map<Class<?>, TableManager<? extends BaseBean>> getCacheManagers() {
		return Collections.unmodifiableMap(cacheManagers);
	}

	/**
	 * 根据目标类型返回对应的支持缓存的 {@link TableManager}实例
	 * @param interfaceClass 目标接口类型
	 * @return {@link TableManager}实例
	 * @throws NoSuchElementException 找不到时抛出异常
	 */
	@SuppressWarnings("unchecked")
	public static final <M extends TableManager<? extends BaseBean>>M 
	getCacheManager(final Class<M>interfaceClass) throws NoSuchElementException {
		checkArgument(interfaceClass != null,"targetType is null");
		TableManager<? extends BaseBean> manager;
		if(null != (manager = cacheManagers.get(interfaceClass))){
			return (M) manager;
		}
		return (M) Iterables.find(cacheManagers.values(), new Predicate<TableManager<?>>() {
	
			@Override
			public boolean apply(TableManager<?> input) {
				return interfaceClass.isInstance(input);
			}
		});
	}

	/**
	 * 根据表记录类型返回支持缓存的数据库操作实例
	 * @param beanType java bean type
	 * @return {@link TableManager}实例,找不到时抛出异常
	 */
	@SuppressWarnings("unchecked")
	public static final <B extends BaseBean> TableManager<B>  
	getCacheManagerByBeanType(Class<?> beanType)  {
		TableManager<? extends BaseBean> manager = cacheBeanTypeManagers.get(beanType);
		return ( TableManager<B>) checkNotNull(manager,"INVALID bean type %s",beanType);
	}

	/**
	 * 根据表名返回支持缓存的数据库操作实例
	 * @param tablename
	 * @return {@link TableManager}实例,找不到时抛出异常
	 */
	@SuppressWarnings("unchecked")
	public static final <B extends BaseBean> TableManager<B> 
	getCacheManager(String tablename)  {
		TableManager<? extends BaseBean> manager = cacheNameManagers.get(tablename);
		return (TableManager<B>) checkNotNull(manager,"INVALID table name %s",tablename);
	}
	/**
	 * 根据表记录类型返回支持缓存的数据库操作实例
	 * @param beanType java bean type
	 * @return {@link CacheManager}实例,找不到时返回{@code null}
	 *  @since 3.30.0
	 */
	@SuppressWarnings({ "rawtypes" })
	public static final CacheManager  
	getCacheManagerByBeanTypeOrNull(Class<?> beanType)  {
		TableManager<? extends BaseBean> manager = cacheBeanTypeManagers.get(beanType);
		if(null != manager) {
			TableManagerDecorator handler = (TableManagerDecorator) Proxy.getInvocationHandler(manager);
			return (CacheManager) handler.delegate;
		}
		return null;
	}
	/**
	 * 根据表名返回支持缓存的数据库操作实例
	 * @param tablename
	 * @return {@link CacheManager}实例,找不到时返回{@code null}
	 * @since 3.30.0
	 */
	@SuppressWarnings({ "rawtypes" })
	public static final CacheManager
	getCacheManagerOrNull(String tablename)  {
		TableManager<? extends BaseBean> manager = cacheNameManagers.get(tablename);
		if(null != manager) {
			TableManagerDecorator handler = (TableManagerDecorator) Proxy.getInvocationHandler(manager);
			return (CacheManager) handler.delegate;
		}
		return null;
	}
	
	/**
	 * 根据表记录类型返回数据库操作实例<br>
	 * 优先返回支持缓存的数据库操作实例(cache)
	 * @param interfaceClass 接口类
	 * @return {@link TableManager}实例,找不到时抛出异常
	 */
	public static <M extends TableManager<? extends BaseBean>>M 
	instanceOf(Class<M>interfaceClass) {
	    try {
	        return getCacheManager(interfaceClass);
	    } catch (Exception e) {
	        return getTableManager(interfaceClass);
	    } 
	}

	/**
	 * 根据表名返回数据库操作实例<br>
	 * 优先返回支持缓存的数据库操作实例(cache)
	 * @param tablename table name
	 * @return {@link TableManager}实例,找不到时抛出异常
	 */
	public static <B extends BaseBean>TableManager<B> managerOf(String tablename) {
	    try {
	        return getCacheManager(tablename);
	    } catch (Exception e) {
	        return getTableManager(tablename);
	    } 
	}

	/**
	 * 将数据库操作实例转换对应的{@link BaseTableManager}实例<br>
	 * @param manager
	 * @return {@link BaseTableManager}实例,转换失败时抛出异常
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	static <B extends BaseBean>BaseTableManager<B> baseManagerOf(TableManager<B> manager){
		checkArgument(manager != null,"manager is null");
		if(manager instanceof BaseTableManager){
			return (BaseTableManager<B>) manager;
		}else if(Proxy.isProxyClass(manager.getClass())){
			InvocationHandler handler = Proxy.getInvocationHandler(manager);
			checkArgument(handler instanceof TableManagerDecorator,"UNKNOW HANDLER TYPE %s",manager.getClass());
			return ((TableManagerDecorator)handler).delegate;
		}else {
			throw new IllegalArgumentException(logString("UNKNOW TableManager instance type %s",manager.getClass()));
		}
	}
	/**
	 * 从数据库操作接口类获取对应的{@link BaseTableManager}实例<br>
	 * @param interfaceClass
	 * @return {@link BaseTableManager}实例,转换失败时抛出异常
	 */
	static <B extends BaseBean,M extends TableManager<B>>BaseTableManager<B> baseManagerOf(Class<M> interfaceClass){
		return baseManagerOf(instanceOf(interfaceClass));
	}
	/**
	 * 根据表名返回对应的{@link BaseTableManager}实例
	 * 优先返回支持缓存的数据库操作实例(cache)
	 * @param tablename table name
	 * @return {@link BaseTableManager}实例,找不到时抛出异常
	 */
	@SuppressWarnings("unchecked")
	static <B extends BaseBean>BaseTableManager<B> baseManagerOf(String tablename) {
	    return baseManagerOf((TableManager<B>)managerOf(tablename)); 
	}

	/**
	 * 根据数据表名(驼峰命名格式)返回对应的{@link TableManager}实例<br>
	 * 优先返回支持缓存的数据库操作实例(cache)
	 * @param coreClass
	 * @param alias 别名
	 * @return {@link TableManager}实例,找不到时抛出异常
	 * @deprecated never used
	 */
	public static  <B extends BaseBean>TableManager<B> managerOfCoreClass(String coreClass, String alias) {
		String tablename = RowMetaData.getRowMetaDataByCoreClassName(coreClass, alias).tablename;
		try {
	        return getCacheManager(tablename);
	    } catch (Exception e) {
	        return getTableManager(tablename);
	    } 
	}

	/**
	 * 根据表记录类型返回数据库操作实例<br>
	 * 优先返回支持缓存的数据库操作实例
	 * @param beanType java bean type
	 * @return {@link TableManager}实例,找不到时抛出异常
	 */
	public static <B extends BaseBean>TableManager<B> managerOf(Class<B> beanType) {
	    try {
	        return getCacheManagerByBeanType(beanType);
	    } catch (Exception e) {
	        return getTableManagerByBeanType(beanType);
	    } 
	}
	public enum Module{
		MANAGER,CACHE,DECORATOR,MANAGERS,BASETABLEMANAGER
	}
	/**
	 * set debug flag that determine if output log message,default : false
	 * @param debug flag for debug message output
	 * @param modules modules array to be set debug flag,all modules used if be null or empty
	 */
	public static void setDebug(boolean debug,Module... modules){
		if(modules == null || modules.length == 0){
			modules = Module.values();
		} 
		for(Module module:modules){
			if(null != module){
				switch (module) {
				case MANAGER:
					DataSourceConfig.setDebugOutput(debug);
					break;
				case CACHE:
					ColumnCache.setDebug(debug);
					break;
				case DECORATOR:
					TableManagerDecorator.setDebug(debug);
					break;
				case MANAGERS:
					Managers.debug = debug;
					break;
				case BASETABLEMANAGER:
					BaseTableManager.setDebug(debug);
					break;
				default:
					break;
				}
			}
		}
	}
	/**
	 * set flags for check if WHRE SQL is legal 
	 * @param whereCheckFlag
	 */
	public static void setWhereCheckFlag(int whereCheckFlag) {
		BaseTableManager.setWhereCheckFlag(whereCheckFlag);
	}
	/**
	 * set debug flag that determine if output log message for Manager instance named {@code alias},default : false
	 * @param alias alias name
	 * @param debug debug flag
	 * @deprecated
	 */
	public static void setDebugOfManager(String alias,boolean debug){
		// DO NOTHING
	}

	/**
	 * 对所有{@link JdbcProperty}定义的属性如果值不为String则删除
	 * @param properties
	 * @return always properties
	 */
	@SuppressWarnings({ "rawtypes"})
	private static Map cleanNotString(Map properties){
		if(null != properties){
			 for(JdbcProperty property : JdbcProperty.values()){
		        	if(!(properties.get(property.key) instanceof String)){
		        		properties.remove(property.key);
		        	}
		        	if(!property.isGlobal()){
		        		String key = property.withPrefix("debug.");
		        		if(!(properties.get(key) instanceof String)){
		        			properties.remove(key);
		        		}
		        		key = property.withPrefix("work.");
		        		if(!(properties.get(key) instanceof String)){
		        			properties.remove(key);
		        		}
		        	}
			 }
		}
		return properties;
	}
    /**
     * 对配置参数进行归一化处理<br>
     * 对于{@link JdbcProperty}定义的所有属性进行归一化处理,如果有带'work.','debug.'前缀的属性,则优先使用,
     * 如果有不带前缀的属性,则将属性名根据'debug'标志改为'work.'或'debug.'前缀的属性<br>
     * 比如: 如果同时存在jdbc.url, work.jdbc.url这两个定义,且debug标志为false(未定义时默认为false),则删除jdbc.url定义
     * 如果只存在jdbc.url定义,且debug标志为false(未定义时默认为false),则将之改为work.jdbc.url
     * @param properties
     * @return new Map instance
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
	static final Map normailze(Map properties){
    	HashMap m = new HashMap(properties);
	    if(null != properties){
	    	cleanNotString(m);
	        if(!m.containsKey(JdbcProperty.DEBUG.key)){
	        	m.put(JdbcProperty.DEBUG.key, "false");
	        }
	        boolean isDebug = Boolean.valueOf((String) m.get(JdbcProperty.DEBUG.key));
	        String prefix = isDebug?"debug.":"work.";
	        for(JdbcProperty property : JdbcProperty.values()){
	        	// 跳过所有全局属性
	        	if(!property.isGlobal()){
	        		if(m.containsKey(property.key)){
	        			Object value = m.remove(property.key);
	        			if(!m.containsKey(property.withPrefix(prefix))){
	        				// 有前缀的属性未定义时使用无前缀的属性代替
	        				m.put(property.withPrefix(prefix), value);
	        			}else{
	        				// 有前缀的属性优先
	        			}
	        		}
	        	}
	        }
	    }
    	return m;
    }

	/**
	 * inject properties to {@link DataSourceConfig#databaseProperties}<br>
	 * be effected only while called before initializing singleton instance 
	 * @param properties
	 * @param prefix the prefix of properties,ignore if {@code null}
	 * @see JdbcProperty
	 */
	@SuppressWarnings("rawtypes")
	public static final void injectProperties(Map properties, String prefix){
        DataSourceConfig.injectProperties(DataSourceConfig.asEnumMap(properties, prefix));
	}
	/**
	 * short cut of {@link #injectProperties(Map, String)}
	 */
	@SuppressWarnings("rawtypes")
	public static final void injectProperties(Map properties){
	    injectProperties(properties, null);
	}
	/**
	 * @return singleton instance of {@link Manager} as {@link SqlRunner} instance
	 */
	public static SqlRunner getSqlRunner(){
		return Manager.getInstance();
	}
	/**
	 * @param alias alias name of connection 
	 * @return singleton instance of {@link Manager} as {@link SqlRunner} instance for alias
	 */
	public static SqlRunner getSqlRunner(String alias){
		Manager manager =  managerInstanceOfAlias(alias);
		return manager == null ? getSqlRunner() : manager;
	}
	/**
	 * get Manager instance with jdbcUrl
	 * @param jdbcUrl JDBC connect string
	 * @return Manager instance OR null if not found 
	 */
	public static Manager managerInstanceOf(String jdbcUrl){
		return null == jdbcUrl ? null : Manager.managers.get(jdbcUrl);
	}

	/**
	 * get Manager instance with alias
	 * @param alias alias name of JDBC connection
	 * @return Manager instance OR null if not found 
	 */
	public static Manager managerInstanceOfAlias(String alias){
		if(Strings.isNullOrEmpty(alias)){
			alias =  DEFAULT_ALIAS;
		}
		return Manager.aliasManagers.get(alias);
	}

	/**
	 * create Manager instance with properties 
	 * @param properties
	 * @return Manager instance 
	 */
	@SuppressWarnings({ "rawtypes" })
	public static synchronized Manager createInstance(Map properties){
		properties = normailze(checkNotNull(properties, "properties is null"));
		String alias = DataSourceConfig.parseValue(properties, JdbcProperty.ALIAS);
		Manager manager = null;
		if(!Strings.isNullOrEmpty(alias)){
			manager = managerInstanceOfAlias(alias);
			if( null != manager){
				return manager;
			}else{
				DataSourceConfig config = DataSourceConfig.createConfig(asProperties(properties));
				return new Manager(config);
			}
		}
		String jdbcUrl = checkNotNull(DataSourceConfig.parseValue(properties, JdbcProperty.JDBC_URL),
				"JDBC connect string is not defined");
		manager = managerInstanceOf(jdbcUrl);
		if(manager == null){
			manager = new Manager(asProperties(properties));
		}
		return manager;
	}
	public  static Manager createInstance(EnumMap<JdbcProperty,String> properties){
		return createInstance(asProperties(properties));
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	private static Properties asProperties(Map properties){
		if(properties instanceof Properties){
			return (Properties)properties;
		}
		Properties props = new Properties();
		if(null != properties){
			props.putAll(properties);
		}
		return props;
	}

	/**
	 * convert {@link EnumMap } properties to {@link Properties}<br>
	 * @param input input properties
	 */
	private static final Properties asProperties(EnumMap<JdbcProperty, String> input){
		Properties properties = new Properties();
		DataSourceConfig.injectToProperties(properties, input);
		return properties;
	}
}
